package top.maof.mfcoin.tx;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import top.maof.mfcoin.block.Block;
import top.maof.mfcoin.block.BlockBody;

import java.util.*;

/**
 * UTXO
 * UnSpending Transaction Output
 * <p>
 * 区块链理论上来说可能会有分叉的情况,
 * 因此,每个分叉都应该有自己的utxo结构
 *
 * @author 毛逢
 * @date 2021/11/11 16:16
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UTXO {

    /**
     * key: input.hash base64编码
     * value: input
     */
    private Map<String, Input> map = new HashMap<>();

    // 当前分支最后的区块
    private String latestHash;


    public UTXO cloneSelf() {
        UTXO utxo = new UTXO();
        utxo.setLatestHash(this.latestHash);
        Map<String, Input> cloneMap = new HashMap<>(this.map);
        utxo.setMap(cloneMap);
        return utxo;
    }

    /**
     * 新区块发布,对应的utxo刷新
     * <p>
     * 方法不验证区块合法性,整个区块是否合法需由其他地方校验
     */
    public void addBlock(Block block) {
        // 该条链的第一个区块
        if (block.getBlockHeader().getPreviousHash() == null && map.size() == 0 && latestHash == null) {
            BlockBody blockBody = block.getBlockBody();
            Set<Map.Entry<String, Transaction>> entries = blockBody.getMerkleTree().entrySet();
            // 第一个区块必须只有一个交易,且必须是铸币交易,交易的输出必须只有一个
            if (entries.size() == 1) {
                for (Map.Entry<String, Transaction> entry : entries) {
                    Transaction tx = entry.getValue();
                    if (tx.isCoinBase()) {
                        List<Output> outputs = tx.getOutputs();
                        if (outputs.size() == 1) {
                            Output output = outputs.get(0);
                            Input input = new Input();
                            input.setAddress(output.getAddress());
                            input.setCoin(output.getCoin());

                            map.put(Input.hashBase64(input), input);
                            this.latestHash = block.getBlockHeader().getHash();
                        }
                    }
                }

            }
        }
        // 不是第一个区块
        else if (block.getBlockHeader().getPreviousHash() != null
                && block.getBlockHeader().getPreviousHash().equals(latestHash)) {
            BlockBody blockBody = block.getBlockBody();
            Set<Map.Entry<String, Transaction>> entries = blockBody.getMerkleTree().entrySet();

            // 移除所有的input,加入output
            for (Map.Entry<String, Transaction> entry : entries) {
                Transaction tx = entry.getValue();
                // 普通交易
                if (!tx.isCoinBase()) {
                    map.remove(tx.getInput().hashCode());
                    List<Output> outputs = tx.getOutputs();
                    for (Output output : outputs) {
                        Input input = new Input();
                        input.setAddress(output.getAddress());
                        input.setCoin(output.getCoin());

                        map.put(Input.hashBase64(input), input);
                    }
                    this.latestHash = block.getBlockHeader().getHash();
                }
                // 铸币交易
                else {
                    List<Output> outputs = tx.getOutputs();
                    if (outputs.size() == 1) {
                        Output output = outputs.get(0);
                        Input input = new Input();
                        input.setAddress(output.getAddress());
                        input.setCoin(output.getCoin());

                        map.put(Input.hashBase64(input), input);
                        this.latestHash = block.getBlockHeader().getHash();
                    }
                }
            }
        }


    }

}
